package org.javaexpert.spaces.util;

import net.jini.core.transaction.Transaction;
import net.jini.core.transaction.TransactionFactory;
import net.jini.core.transaction.server.TransactionManager;
import net.jini.lease.LeaseListener;
import net.jini.lease.LeaseRenewalEvent;
import net.jini.lease.LeaseRenewalManager;
import org.apache.commons.logging.LogFactory;

/**
 * Created by IntelliJ IDEA.
 * User: piotrga
 * Date: Dec 18, 2007
 * Time: 2:41:41 PM
 * To change this template use File | Settings | File Templates.
 */
public class TransactionTemplate {
    private TransactionManager txnMgr;
    private LeaseRenewalManager leaseRenewalManager = new LeaseRenewalManager();

    public TransactionTemplate(TransactionManager txnMgr) {
        this.txnMgr = txnMgr;
    }

    public  Transaction getTransaction() {
        return getTransaction(1000L * 60 * 10);
    }

    public Transaction getTransaction(Long transactionLease) {
        try {
            return TransactionFactory.create(txnMgr, transactionLease).transaction;
        } catch (Exception e) {
            throw  new RuntimeException(e);
        }
    }

    public Transaction.Created getTransactionAdvanced(Long transactionLease) {
        try {
            return TransactionFactory.create(txnMgr, transactionLease);
        } catch (Exception e) {
            throw  new RuntimeException(e);
        }
    }

    public void runInTxn(RunnableInTxn runnable) {
        Transaction txn = getTransaction();
        runInTxn(txn, runnable);

    }

    public void runInTxn(Long transactionLease, Long maximumTime, RunnableInTxn runnable) {
        Transaction.Created created = getTransactionAdvanced(transactionLease);
        leaseRenewalManager.renewFor(created.lease, maximumTime, transactionLease/2, new LeaseListener() {
            public void notify(LeaseRenewalEvent event) {
                LogFactory.getLog("renewal").debug("event="+event.getClass()+", exception="+event.getException());
            }
        } );
        runInTxn(created.transaction, runnable);

    }

    private void runInTxn(Transaction txn, RunnableInTxn runnable) {
        assert txn != null;
        assert runnable != null;
        try {
            runnable.run(txn);
        } catch (Exception e) {
            abort(txn);
            throw new RuntimeException(e);
        }
        try {
            txn.commit();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    private <T> T runInTxn(Transaction txn, CallableInTxn<T> runnable) {
        T res;
        try {
            res = runnable.call(txn);
        } catch (Exception e) {
            abort(txn);
            throw new RuntimeException(e);
        }
        try {
            txn.commit();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return res;
    }

    public void abort(Transaction txn) {
        try {
            txn.abort();
        } catch (Exception e1) {
            throw new RuntimeException(e1);
        }
    }

    public <T> T runInTxn(CallableInTxn<T> callable) {
        return runInTxn(getTransaction(), callable);
    }
}
